package cbbx_sm.decision_maker.search;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;

import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.ProbabilisticModel;
import cbbx_sm.utils.ExperimentManager;


public class WorldStateTransition implements Serializable{
	
	private ProbabilisticModel model;
	private Schedules schedules;
	
	// for testing purposes.
	public static double motion_given_nomotion = 0.05d;
	public static double motion_given_motion = 0.65d;
	//protected  States states = null;


	public WorldStateTransition(ProbabilisticModel paramModel) throws Exception{
		model = paramModel;
		initSchedulesBasedOnModel(model);
	}
	
	/**
	 * Computes the probability of the scheduler "seeing" the states that it has seen.
	 * 
	 * @param parentCiEvent
	 * @param CiEvent
	 * @param childSchedule
	 * @return
	 */
	public double getTransitionProbability(double[] parentCiEvent,
			double[] CiEvent, String childSchedule) {
		double[] modelEvent = rollProbabilitiesForward(parentCiEvent);
		List<Cluster> clusters = model.getFusedClusters();
		double probability = 1;
		for (int to_index=0; to_index<CiEvent.length; to_index++){
			if ((childSchedule.equals(Schedules.UP)) || 
					(clusters.get(to_index).getUniqueId().compareTo(childSchedule) == 0)){
				double probabilitySeeingMotion = modelEvent[to_index];
				
				if (CiEvent[to_index]==Event.p1){
					probability *= probabilitySeeingMotion;
				}
				if (CiEvent[to_index]==Event.p0){
					probability *= 1-probabilitySeeingMotion;
				}				
			}
		}
		return probability;
	}
	
	public double[] rollProbabilitiesForward(double[] ciEvent) {
		double[] newEventsWithoutApproximation = new double[ciEvent.length];
		List<Cluster> clusters = model.getFusedClusters();
		for (int to_index=0; to_index<ciEvent.length; to_index++){
			double probability = 1;
			if (ExperimentManager.useAprioriWhenRollingForward) {
				probability = 1-model.getAPrioriProbability(clusters.get(to_index)); 
			}

			for (int from_index=0; from_index<ciEvent.length; from_index++){
				boolean seenEvent = ciEvent[from_index] == Event.p1 || ciEvent[from_index] == Event.p0;
				if (ExperimentManager.useNonSeenMotion || seenEvent) {
					probability *= 1-model.getIndependentTimeCorrelationProbability(1, ciEvent[from_index], clusters.get(from_index), clusters.get(to_index));					
				}
			}

			probability = 1-probability;

			newEventsWithoutApproximation[to_index] = probability;

		}
		
		return newEventsWithoutApproximation;
		
	}
	
	public void setRealWorldModel2States(ProbabilisticModel singleCamModel) throws Exception {
		model = singleCamModel;
		initSchedulesBasedOnModel(singleCamModel);
	}
	
    private void initSchedulesBasedOnModel(ProbabilisticModel model) throws Exception {
		int numberOfClusters = model.getFusedClusters().size();
		String[] schedules = new String[numberOfClusters+1];
		for (int i=0; i < numberOfClusters; i++){
			schedules[i] = model.getFusedClusters().get(i).getUniqueId();
		}
		schedules[numberOfClusters] = Schedules.UP;
		this.schedules = new Schedules(schedules);
		//double[] possibleValues = {0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1};
		//EventFactory.createEventInstance(possibleValues);		
	}

    public WorldStateTransition(int regions, int timeWindow){
      this(regions, motion_given_motion, motion_given_nomotion, timeWindow);
    }
	public WorldStateTransition(int regions, double motionmotion, double motionnomotion, int timeWindow){
		motion_given_motion = motionmotion;
		motion_given_nomotion = motionnomotion;
		String[] schedules = new String[regions+1];
		List<Cluster> clusters = new ArrayList<Cluster>();
		LinkedHashMap<String, Integer> clustersIndex = new LinkedHashMap<String, Integer>();
		int i=0;
		for (; i<regions; i++){
			Cluster c = new Cluster("C" + i);
			c.setX(i);
			c.setY(i);
			c.setRadius(i);
			clusters.add(c);
			clustersIndex.put(c.getUniqueId(), i);
			schedules[i] = c.getUniqueId();

		}
		schedules[i] = Schedules.UP;
		
		try {
			this.schedules = new Schedules(schedules);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		double[] aprioriProbability;
		double[][][][] correlationProbability;
		
		aprioriProbability = new double[this.schedules.getSchedules().length-1];
		for (i=0; i<this.schedules.getSchedules().length-1; i++){
			aprioriProbability[i] = motion_given_nomotion;
		}
		
		correlationProbability = new double[2][2][this.schedules.getSchedules().length-1][this.schedules.getSchedules().length-1];
		
		for (int motion = 0; motion <=1; motion++) {
			for (i=0; i<this.schedules.getSchedules().length-1; i++){
				correlationProbability[motion][1][i][i] = (motion == 1) ? motion_given_motion : motion_given_nomotion;
			}			
		}
		
		model = new ProbabilisticModel(aprioriProbability, correlationProbability, clustersIndex,clusters, timeWindow);
		try {
      initSchedulesBasedOnModel(model);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
	}
	
	public String print(){
		return model.toString();
	}

	public ProbabilisticModel getModel() {
		return model;
	}

	public Hashtable<Cluster, Integer> getClusterIndexHash() {
		Hashtable<Cluster, Integer> clusterHash = new Hashtable<Cluster, Integer>();
		List<Cluster> clist =  getModel().getFusedClusters();
		for (int i=0; i< clist.size(); i++){
			clusterHash.put(clist.get(i), i);
		}
		return clusterHash;
	}

	public String[] getSchedules() {
		return this.schedules.getSchedules();
	}

	public int scheduleToCameraId(String scheduleSelected) {
		return this.schedules.scheduleToCameraId(scheduleSelected);
	}
}
